﻿using System;
using System.Collections.Generic;
using System.Data;
using Order.Domain;
using System.Linq;
using System.Net.Sockets;
using System.Threading.Tasks;
using Dapper;
using DotNetCore.CAP;
using Newtonsoft.Json;
using Polly;
using System.Data.SqlClient;
using System.IO;
using System.Collections.Concurrent;
using Order.Common.Logger;

namespace Order.Common
{
    public class PublishDomainEventService : IPublishDomainEventService
    {
        private readonly ICapPublisher _capPublisher;
        private readonly ILoggerHelper _loggerHelper;
        private readonly string ConnectionStr = ConfigurationHelper.GetConnectionString();
        public PublishDomainEventService(ICapPublisher capPublisher, ILoggerHelper loggerHelper)
        {
            _capPublisher = capPublisher;
            _loggerHelper = loggerHelper;
        }

        /// <summary>
        /// 存储聚合根中的事件到EventStorage 发布事件
        /// </summary>
        /// <typeparam name="TAggregationRoot"></typeparam>
        /// <param name="event"></param>
        /// <returns></returns>
        public async Task AppendEventStoragePublishEventAsync<TAggregationRoot>(TAggregationRoot @event)
            where TAggregationRoot : IAggregationRoot
        {
            var domainEventList = @event.UncommittedEvents.ToList();
            if (domainEventList.Count == 0)
            {
                throw new Exception("请添加事件!");
            }

            await TryAppendEventStorageAsync(domainEventList).ContinueWith(async e =>
            {
                if (e.Result == (int)EventStorageStatus.Success)
                {
                    await TryPublishDomainEventAsync(domainEventList).ConfigureAwait(false);
                    @event.ClearEvents();
                }
            });
        }

        /// <summary>
        /// 发布领域事件
        /// </summary>
        /// <returns></returns>
        public async Task PublishDomainEventAsync(List<IDomainEvent> domainEventList)
        {
            using (var connection =
                new SqlConnection(ConnectionStr))
            {
                if (connection.State == ConnectionState.Closed)
                {
                    await connection.OpenAsync().ConfigureAwait(false);
                }
                using (var transaction = await connection.BeginTransactionAsync().ConfigureAwait(false))
                {
                    try
                    {
                        if (domainEventList.Count > 0)
                        {
                            foreach (var domainEvent in domainEventList)
                            {
                                await _capPublisher.PublishAsync(domainEvent.GetRoutingKey(), domainEvent).ConfigureAwait(false);
                            }
                        }
                        await transaction.CommitAsync().ConfigureAwait(false);
                    }
#pragma warning disable CS0168 // 声明了变量“e”，但从未使用过
                    catch (Exception e)
#pragma warning restore CS0168 // 声明了变量“e”，但从未使用过
                    {
                        await transaction.RollbackAsync().ConfigureAwait(false);
                        throw;
                    }
                }
            }
        }

        /// <summary>
        /// 发布领域事件重试
        /// </summary>
        /// <param name="domainEventList"></param>
        /// <returns></returns>
        public async Task TryPublishDomainEventAsync(List<IDomainEvent> domainEventList)
        {
            var policy = Policy.Handle<SocketException>().Or<IOException>().Or<Exception>()
                .RetryForeverAsync(onRetry: exception =>
                {
                    Task.Factory.StartNew(() =>
                    {
                        //记录重试的信息
                        _loggerHelper.LogInfo("发布领域事件异常", exception.Message);
                    });
                });
            await policy.ExecuteAsync(async () =>
            {
                await PublishDomainEventAsync(domainEventList).ConfigureAwait(false);
            });

        }

        /// <summary>
        /// 存储聚合根中的事件到EventStorage中
        /// </summary>
        /// <returns></returns>
        public async Task<int> AppendEventStorageAsync(List<IDomainEvent> domainEventList)
        {
            if (domainEventList.Count == 0)
            {
                throw new Exception("请添加事件!");
            }
            var status = (int)EventStorageStatus.Failure;
            using (var connection = new SqlConnection(ConnectionStr))
            {
                try
                {
                    if (connection.State == ConnectionState.Closed)
                    {
                        await connection.OpenAsync().ConfigureAwait(false);
                    }
                    using (var transaction = await connection.BeginTransactionAsync().ConfigureAwait(false))
                    {
                        try
                        {
                            if (domainEventList.Count > 0)
                            {
                                foreach (var domainEvent in domainEventList)
                                {
                                    EventStorage eventStorage = new EventStorage
                                    {
                                        Id = Guid.NewGuid().ToString(),
                                        
                                        AggregateRootId = domainEvent.AggregateRootId,
                                        AggregateRootType = domainEvent.AggregateRootType,
                                        CreateDateTime = domainEvent.CreateDateTime,
                                        Version = domainEvent.Version,
                                        EventData = Events(domainEvent)
                                    };
                                    var eventStorageSql =
                                        $"INSERT INTO EventStorageInfo(Id,AggregateRootId,AggregateRootType,CreateDateTime,Version,EventData) VALUES (@Id,@AggregateRootId,@AggregateRootType,@CreateDateTime,@Version,@EventData)";
                                    await connection.ExecuteAsync(eventStorageSql, eventStorage, transaction).ConfigureAwait(false);
                                }
                            }
                            await transaction.CommitAsync().ConfigureAwait(false);
                            status = (int)EventStorageStatus.Success;
                        }
#pragma warning disable CS0168 // 声明了变量“e”，但从未使用过
                        catch (Exception e)
#pragma warning restore CS0168 // 声明了变量“e”，但从未使用过
                        {
                            await transaction.RollbackAsync().ConfigureAwait(false);
                            throw;
                        }
                    }

                }
#pragma warning disable CS0168 // 声明了变量“e”，但从未使用过
                catch (Exception e)
#pragma warning restore CS0168 // 声明了变量“e”，但从未使用过
                {
                    connection.Close();
                    throw;
                }
            }
            return status;
        }

        /// <summary>
        /// AppendEventStorageAsync异常重试
        /// </summary>
        public async Task<int> TryAppendEventStorageAsync(List<IDomainEvent> domainEventList)
        {
            var policy = Policy.Handle<SocketException>().Or<IOException>().Or<Exception>()
                .RetryForeverAsync(onRetry: exception =>
                {
                    Task.Factory.StartNew(() =>
                    {
                        //记录重试的信息
                        _loggerHelper.LogInfo("存储事件异常", exception.Message);
                    });
                });
            var result = await policy.ExecuteAsync(async () =>
              {
                  var resulted = await AppendEventStorageAsync(domainEventList).ConfigureAwait(false);
                  return resulted;
              });
            return result;
        }

        /// <summary>
        /// 根据DomainEvent序列化事件Json
        /// </summary>
        /// <param name="domainEvent"></param>
        /// <returns></returns>
        public string Events(IDomainEvent domainEvent)
        {
            ConcurrentDictionary<string, string> dictionary = new ConcurrentDictionary<string, string>();
            //获取领域事件的类型（方便解析Json）
            var domainEventTypeName = domainEvent.GetType().Name;
            var domainEventStr = JsonConvert.SerializeObject(domainEvent);
            dictionary.GetOrAdd(domainEventTypeName, domainEventStr);
            var eventData = JsonConvert.SerializeObject(dictionary);
            return eventData;
        }

    }

    public enum EventStorageStatus
    {
        // <summary>
        // 成功
        // </summary>
        Success = 0,
        // <summary>
        // 失败
        // </summary>
        Failure = 1,
        // <summary>
        // 重复
        // </summary>
        Repeat = 2,

    }
}
